<template>
  <div class="p-2.5">
    <p class="flex justify-center mb-4 text-2xl font-bold">串口通信</p>
    <div class="h-[500px]">
      <a-row :gutter="20" class="h-full">
        <a-col :span="6">
          <div class="flex flex-col gap-5 p-2.5 border h-full rounded-md">
            <div>串口设置</div>
            <!-- 端口 -->
            <div class="relative flex items-center gap-2">
              <div class="min-w-12">端 口</div>
              <a-select
                class="flex-1"
                allow-clear
                :disabled="isOpen"
                show-search
                v-model:value="port"
                placeholder="请选择串口"
                :options="portList"
                :field-names="{ value: 'path', label: 'path' }"
              ></a-select>
            </div>
            <!-- 波特率 -->
            <div class="flex items-center gap-2">
              <div class="min-w-12">波特率</div>
              <a-select
                class="flex-1"
                allow-clear
                show-search
                :disabled="isOpen"
                v-model:value="baudRate"
                placeholder="请选择波特率"
                :options="baudRateList"
              >
              </a-select>
            </div>
            <!-- 数据位 -->
            <div class="flex items-center gap-2">
              <div class="min-w-12">数据位</div>
              <a-select
                class="flex-1"
                allow-clear
                show-search
                :disabled="isOpen"
                v-model:value="dataBit"
                placeholder="请选择数据位"
                :options="dataBitList"
              >
              </a-select>
            </div>
            <!-- 校验位 -->
            <div class="flex items-center gap-2">
              <div class="min-w-12">校验位</div>
              <a-select
                class="flex-1"
                allow-clear
                show-search
                :disabled="isOpen"
                v-model:value="parityBit"
                placeholder="请选择校验位"
                :options="parityBitList"
              >
              </a-select>
            </div>
            <!-- 停止位 -->
            <div class="flex items-center gap-2">
              <div class="min-w-12">停止位</div>
              <a-select
                class="flex-1"
                allow-clear
                show-search
                :disabled="isOpen"
                v-model:value="stopBit"
                placeholder="请选择停止位"
                :options="stopBitList"
              >
              </a-select>
            </div>
            <div class="flex justify-end mt-5">
              <a-button type="primary" @click="sacnPort">扫描串口</a-button>
              <a-button class="ml-2" v-if="!isOpen" type="primary" @click="handleOpen">打开</a-button>
              <a-button class="ml-2" v-else type="primary" danger @click="handleClose">关闭</a-button>
              <a-button class="ml-2" type="primary" danger @click="handleClearLog('all')">清空日志</a-button>
            </div>
          </div>
        </a-col>
        <a-col :span="9">
          <div class="flex flex-col h-full gap-2">
            <a-textarea id="textarea" class="flex-1 resize-none" v-model:value="logMsg" :rows="4" readonly />
            <div class="flex items-center gap-2">
              <a-input v-model:value="sendMsg" placeholder="发送数据"></a-input>
              <a-button type="primary" @click="handleSendMsg">发送</a-button>
            </div>
          </div>
        </a-col>
        <a-col :span="9">
          <a-textarea class="h-full resize-none" v-model:value="receiveMsg" :rows="4" readonly />
        </a-col>
      </a-row>
    </div>
  </div>
</template>

<script setup lang="ts">
  import { SerialPort, SerialPortMock } from 'serialport'
  import { message } from 'ant-design-vue'

  const port = ref('')
  const portList = ref<any[]>([])
  const serialport = ref<SerialPort>()

  // 波特率
  const baudRate = ref(9600)
  const baudRateList = [
    {
      label: '9600',
      value: 9600
    },
    {
      label: '19200',
      value: 19200
    },
    {
      label: '38400',
      value: 38400
    },
    {
      label: '57600',
      value: 57600
    },
    {
      label: '115200',
      value: 115200
    }
  ]

  // 数据位
  const dataBit = ref(8)
  const dataBitList = [
    {
      label: '5',
      value: 5
    },
    {
      label: '6',
      value: 6
    },
    {
      label: '7',
      value: 7
    },
    {
      label: '8',
      value: 8
    }
  ]

  // 校验位
  const parityBit = ref('None')
  const parityBitList = [
    {
      label: 'None',
      value: 'None'
    },
    {
      label: 'Even',
      value: 'Even'
    },
    {
      label: 'Odd',
      value: 'Odd'
    },
    {
      label: 'Mark',
      value: 'Mark'
    },
    {
      label: 'Space',
      value: 'Space'
    }
  ]

  // 停止位
  const stopBit = ref(1)
  const stopBitList = [
    {
      label: '1',
      value: 1
    },
    {
      label: '1.5',
      value: 1.5
    },
    {
      label: '2',
      value: 2
    }
  ]

  const isOpen = ref(false)

  const sendMsg = ref('')
  const receiveMsg = ref('')
  const logMsg = ref('')

  // 扫描串口
  const sacnPort = async () => {
    let res = await SerialPort.list()
    if (res.length) {
      port.value = res[0].path
    }
    let mock = await SerialPortMock.list()
    console.log(res, '扫描到的串口')
    portList.value = res.concat(mock)
  }
  sacnPort()

  // 打开串口
  const handleOpen = () => {
    serialport.value = new SerialPort(
      {
        path: port.value,
        baudRate: baudRate.value,
        dataBits: dataBit.value,
        parity: parityBit.value,
        stopBits: stopBit.value,
        autoOpen: true
      },
      (err) => {
        if (err) {
          console.log(err, '打开串口失败')
          message.error(`打开${port.value}串口失败！` + err)
          return
        }
        unref(serialport)?.on('data', (data: Buffer) => {
          console.log('收到的数据:', data.toString())
          receiveMsg.value += data.toString()
        })

        unref(serialport)?.on('error', (err) => {
          if (err) {
            console.log(err, 'error')
          }
        })
      }
    )
  }

  // 发送数据
  const handleSendMsg = () => {
    unref(serialport)?.write(unref(sendMsg), (err) => {
      if (err) {
        console.log('写入串口数据失败', err)
        return message.error(`写入${port}串口数据失败！` + err)
      }
      logMsg.value += sendMsg.value + '\n'
      sendMsg.value = ''
    })
  }

  // 清空日志
  const handleClearLog = (type: 'send' | 'receive' | 'all') => {
    if (type == 'send') {
      logMsg.value = ''
    } else if (type == 'receive') {
      receiveMsg.value = ''
    } else {
      logMsg.value = ''
      receiveMsg.value = ''
    }
  }

  // 关闭串口
  const handleClose = () => {
    unref(serialport)?.close()
  }

  // 触底
  const handleScrollToEnd = async () => {
    await nextTick()
    const textarea = document.getElementById('textarea') as HTMLTextAreaElement
    if (textarea) {
      textarea.scrollTop = textarea.scrollHeight
    }
  }

  // 监听串口
  watch(
    () => serialport,
    () => {
      if (unref(serialport)) {
        isOpen.value = unref(serialport)!.isOpen
      }
    },
    { immediate: true, deep: true }
  )

  watch(
    () => logMsg,
    () => {
      handleScrollToEnd()
    },
    {
      deep: true,
      immediate: true
    }
  )

  // 页面刷新，关闭串口
  window.addEventListener('beforeunload', () => {
    handleClose()
  })
</script>

<style scoped></style>
